package hxm.core;

import haxe.macro.Context;
import haxe.macro.Expr;
import hxm.core.MExpr;
import hxm.core.Resolver;
import hxm.monads.OptionMonad;
import tink.macro.tools.MacroTools;

/**
 * ...
 * @author Simon Krajewski
 */

#if macro
using tink.macro.tools.ExprTools;
using tink.core.types.Outcome;
#end

class MacroHelper 
{
	#if macro
	
	static public function getMetaStringValues(type:haxe.macro.Type, name:String):Array<String>
	{
		var ret = [];
		switch(type)
		{
			case TInst(mType, mParam):
				for (meta in mType.get().meta.get())
				{
					if (meta.name == name)
					{
						for (p in meta.params)
						{
							switch(p.getString())
							{
								case Success(value):
									ret.push(value);
								case Failure(err):
									
							}
						}
					}
				}
			default:
		}
		return ret;
	}
	
	static public function getTypeInfo(type:haxe.macro.Type):Option<TypeInfo>
	{
		switch (type)
		{
			case TInst(mType, mParam):
				var name = mType.get().name;
				var parse = Context.parse("Type.createEmptyInstance(" +name + ")", Context.currentPos());
				return Some({ name:name, subtype:(mParam.length == 0 ? None : getTypeInfo(mParam[0])), neutral:parse });
			case TEnum(mType, mParam):
				var sub = getTypeInfo(mParam[0]);
				return Some({ name:mType.get().name, subtype:(mParam.length == 0 ? None : getTypeInfo(mParam[0])), neutral:Context.parse("Type.createEnumIndex(" +mType.get().name+ ", 0)", Context.currentPos()) });
			case TType(mType, mParam):
				var name = mType.get().name;
				if (name.charAt(0) == '#')
					name = name.substr(1);
				return Some( { name:name, subtype:(mParam.length == 0 ? None : getTypeInfo(mParam[0])), neutral:Context.parse("Type.createEmptyInstance(" +name+ ")", Context.currentPos()) });
			default:
				return None;
		}
	}
	
	static public function getIdent<T>(m:MExpr):String
	{
		switch(m.expr)
		{
			case MExpr(expr):
				return expr.getIdent().data();
			case MBlock(b):
				if (b.length == 1)
					return getIdent(b[0]);
			default:
		}
		return Context.error("Could not extract ident from " +m, Context.currentPos());
	}
		
	static public function getMethodString(cls:Option<MonadicClass>, name:String):Option<String>
	{
		switch(cls)
		{
			case None:
				return None;
			case Some(c):
				switch(c.type)
				{
					case TInst(t, _):
						for (field in t.get().statics.get())
							if (field.name == name) return Some(c.path + "." +name);
					default:
						return None;
				}
				return None;
		}
		
		
	}
	
	static public function at<T>(m:MExprBase<T>, p:Position) return { expr:m, pos:p }
	
	static public function getNeutralType()
	{
		return switch(MacroHelper.getTypeInfo(Context.getType("Dynamic")))
		{
			case None:
				throw "Internal error.";
			case Some(typeInfo):
				typeInfo;
		}
	}
	
	static public function cloneMonadicContext(ctx:MonadicContext, ?type:Option<TypeInfo>)
	{
		if (type == null) type = ctx.type;
		var idents = new Hash();
		for (i in ctx.idents.keys())
			idents.set(i, ctx.idents.get(i));
			
		return {
			type: type,
			idents:idents
		};
	}
	
	#end
}

